package com.sinsz.wxpn.open.api;

import com.fcibook.quick.http.QuickHttp;
import com.sinsz.common.exception.ApiException;
import com.sinsz.wxpn.Handler;
import com.sinsz.wxpn.Version;
import com.sinsz.wxpn.WxpnBean;
import com.sinsz.wxpn.WxpnCryptBean;
import com.sinsz.wxpn.open.support.FormaterUtils;
import com.sinsz.wxpn.properties.WxpnProperties;
import com.sinsz.wxpn.support.Constant;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.nutz.json.Json;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * 开发接口 - 用于微信公众号服务器设置校验与消息统一接收。
 * @author chenjianbo
 * @date 2018-11-13
 */
@Controller
@RequestMapping("/{api:[a-z]+}/" + Version.version + "/oa")
public class OpenApiController {

    private final WxpnBean wxpnBean;
    private final WxpnCryptBean wxpnCryptBean;
    private final WxpnProperties properties;

    @Autowired
    public OpenApiController(WxpnBean wxpnBean, WxpnCryptBean wxpnCryptBean, WxpnProperties properties) {
        this.wxpnBean = wxpnBean;
        this.wxpnCryptBean = wxpnCryptBean;
        this.properties = properties;
    }

    /**
     * 服务器设置配置接口
     * <p>
     *     示例接口地址：/api/1.0/oa/fetch/message
     * </p>
     * @param api               动态地址
     * @param signature         消息来源合法验签
     * @param timestamp         时间戳
     * @param nonce             随机串
     * @param encryptType       加密类型
     * @param msgSignature      消息加密验签
     * @param echostr           验证串
     * @param request           请求参数
     * @return                  结果或回复消息
     */
    @RequestMapping(
            value = "/fetch/message",
            method = {RequestMethod.GET, RequestMethod.POST},
            produces = {MediaType.APPLICATION_JSON_UTF8_VALUE,MediaType.APPLICATION_XML_VALUE}
            )
    @ResponseBody
    public String fetchMessage(@PathVariable String api,
                               @RequestParam(required = false) String signature,
                               @RequestParam(required = false) String timestamp,
                               @RequestParam(required = false) String nonce,
                               @RequestParam(value = "encrypt_type", required = false) String encryptType,
                               @RequestParam(value = "msg_signature", required = false) String msgSignature,
                               @RequestParam(required = false) String echostr,
                               HttpServletRequest request) {
        if ("post".equalsIgnoreCase(request.getMethod())) {
            //接收消息
            String xmlContent = wxpnBean.getAccept().fetchMessage(request, timestamp, nonce, signature);
            String message = wxpnCryptBean.decrypt(xmlContent, msgSignature, timestamp, nonce);
            //消息处理
            Handler messageHandler = wxpnBean.getHandler();
            String result = "";
            if (messageHandler != null) {
                message = StringUtils.isEmpty(message)? "{}" : FormaterUtils.formatReceiveMessage(message);
                result = messageHandler.handler(message, msgSignature, timestamp, nonce, properties.getWechatId());
            }
            return StringUtils.isEmpty(result) ? "" : result;
        } else {
            //服务器设置 - 首次校验
            return echostr;
        }
    }

    /**
     * 静默获取授权
     * @param api       动态地址
     * @param request   请求参数
     * @return
     * @throws Exception
     */
    @GetMapping("/fetch/base")
    public String snsapiBase(@PathVariable String api, HttpServletRequest request) throws Exception {
        String uri = request.getRequestURI();
        StringBuffer url = request.getRequestURL();
        String http = url.toString().replace(uri, "") + "/" + api + "/" + Version.version + "/oa/fetch/index";
        return "redirect:" + Constant.OAUTH_2_AUTHORIZE + "?appid=" + properties.getAppId() +
                "&redirect_uri=" + URLEncoder.encode(http, Charsets.UTF_8.name()) +
                "&response_type=code" +
                "&scope=snsapi_base" +
                "#wechat_redirect";
    }

    /**
     * 用户主动获取授权
     * @param api       动态地址
     * @param tag       用户自定义参数（可用于标识业务系统用户和openid关联标识）
     * @param request   请求参数
     * @return
     * @throws Exception
     */
    @GetMapping("/fetch/userinfo")
    public String snsapiUserinfo(@PathVariable String api, @RequestParam(defaultValue = "") String tag, HttpServletRequest request) throws Exception {
        String uri = request.getRequestURI();
        StringBuffer url = request.getRequestURL();
        String http = url.toString().replace(uri, "") + "/" + api + "/" + Version.version + "/oa/fetch/index";
        if (tag.length() > 128) {
            throw new ApiException(Constant.DEFAULT_ERRORCODE,"用户携带标识Tag超出长度限制.");
        }
        return "redirect:" + Constant.OAUTH_2_AUTHORIZE + "?appid=" + properties.getAppId() +
                "&redirect_uri=" + URLEncoder.encode(http, Charsets.UTF_8.name()) +
                "&response_type=code" +
                "&scope=snsapi_userinfo" + (StringUtils.isEmpty(tag) ? "" : "&state=" + tag) +
                "#wechat_redirect";
    }

    /**
     * 授权成功跳转
     * <p>
     *     返回值签名方式：SHA1（ openid + tag + timestamp + (微信公众号开发者密码) ）
     * </p>
     * @param request
     * @param api
     * @return
     */
    @GetMapping("/fetch/index")
    public String fetchOpenid(HttpServletRequest request, @PathVariable String api) {
        if (StringUtils.isEmpty(properties.getOauth())) {
            throw new ApiException(Constant.DEFAULT_ERRORCODE,"未配置授权跳转地址.");
        }
        return fetchOauth(request);
    }

    /**
     * 处理授权请求
     * @param request
     * @return
     */
    private String fetchOauth(HttpServletRequest request) {
        String code = request.getParameter("code");
        String state = request.getParameter("state");
        if (StringUtils.isEmpty(code)) {
            throw new ApiException(Constant.DEFAULT_ERRORCODE,"获取用户授权失败,请重试.");
        }
        Map<String, String> param = new HashMap<>(4);
        param.put("appid", properties.getAppId());
        param.put("secret", properties.getAppSecret());
        param.put("code", code);
        param.put("grant_type", "authorization_code");
        String result = new QuickHttp().url(Constant.OAUTH_2_TOKEN).get().addParames(param).text();
        Map<String, String> map = Json.fromJsonAsMap(String.class, result);
        String defKey = "openid";
        if (!map.containsKey(defKey)) {
            throw new ApiException(Constant.DEFAULT_ERRORCODE,"扫码获取用户授权失败,请重试.");
        }
        String openid = map.getOrDefault(defKey, "");
        String tag = StringUtils.isEmpty(state) ? "" : state;
        String timestamp = String.valueOf(System.currentTimeMillis());
        String sign = FormaterUtils.signatureSha1(openid + tag + timestamp + properties.getAppSecret());
        return "redirect:" + properties.getOauth() + "?openid=" + openid + "&tag=" + tag + "&timestamp=" + timestamp + "&sign=" + sign;
    }

}
